//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "srvobj.h"
#include "cservice.h"
#include <aura.h>

#define Lock(hCritical)     do { \
                                AuraCriticalSection_Enter(hCritical); \
                            } while(0)

#define Unlock(hCritical)   do { \
                                AuraCriticalSection_Leave(hCritical); \
                            } while(0)

ServiceWaiter::~ServiceWaiter()
{
    m_waiterName = NULL;
    if (NULL != m_hNotifyEvent) {
        AuraEvent_Destroy(m_hNotifyEvent);
        m_hNotifyEvent = NULL;
    }
}

ECode ServiceWaiter::Initialize()
{
    return (ECode)AuraEvent_Create(FALSE, AURA_UNSIGNALED, &m_hNotifyEvent);
}

ECode ServiceWaiter::TryWait(
    /* [in]  */ Millisecond32 ms,
    /* [out] */ WaitResult * pResult)
{
    return (ECode)AuraEvent_Wait(m_hNotifyEvent, (Aura_Millisecond32)ms, (Aura_WaitResult *)pResult, AURA_NULL);
}

ECode ServiceWaiter::Notify()
{

    return (ECode)AuraEvent_Signal(m_hNotifyEvent, AURA_SIGNALED);
}

// --------------------------------------------------------------

CServiceCentral * s_pServiceCentral = NULL;

CServiceCentral::CServiceCentral()
{
    m_srvWaitersList.Initialize();
    m_hSrvWaitersListLock = NULL;
    m_hServiceLock        = NULL;
}

CServiceCentral::~CServiceCentral()
{
    if (NULL != m_hServiceLock) {
        AuraCriticalSection_Destroy(m_hServiceLock);
        m_hServiceLock = NULL;
    }

    if (NULL != m_hSrvWaitersListLock) {
        AuraCriticalSection_Destroy(m_hSrvWaitersListLock);
        m_hSrvWaitersListLock = NULL;
    }
}

ECode CServiceCentral::Initialize()
{
    ECode ec = NOERROR;

    ec = AuraCriticalSection_Create(&m_hServiceLock);
    if (FAILED(ec)) {
        return ec;
    }

    ec = AuraCriticalSection_Create(&m_hSrvWaitersListLock);
    if (FAILED(ec)) {
        AuraCriticalSection_Destroy(m_hServiceLock);
        m_hServiceLock = NULL;
        return ec;
    }
    return ec;
}

ECode CServiceCentral::Register(
    /* [in] */ ProcessId processId,
    /* [in] */ PEMuid pClsId,
    /* [in] */ PEIID pIid,
    /* [in] */ UInt32 uIndex,
    /* [out] */ oid_t * pOid)
{
    Object * pObject = NULL;

    pObject = new Object();
    if (NULL == pObject) {
        return E_OUT_OF_MEMORY;
    }

    //May Be BUG: skipped the parameter PEIID.
    pObject->Initialize(processId, pClsId, uIndex, NULL);
    Object::S_AddObject(pObject);

    if (NULL != pOid) {
        *pOid = pObject->m_oid;
    }

    return NOERROR;
}

ECode CServiceCentral::Unregister(
    /* [in] */ ProcessId processId,
    /* [in] */ oid_t oid)
{
    return Object::S_DeleteObject(processId, oid);
}

void CServiceCentral::NotifyServiceWaiters(
    /* [in] */ WString name)
{
    ServiceWaiter *pWaiter = NULL;

    assert(!name.IsNullOrEmpty());

    Lock(m_hSrvWaitersListLock);
    ForEachDLinkNode(ServiceWaiter*, pWaiter, &m_srvWaitersList)
    {
        if (!pWaiter->m_waiterName.Compare(name)) {
            pWaiter->Notify();
        }
    }
    Unlock(m_hSrvWaitersListLock);

    return ;
}

ECode CServiceCentral::RegisterCommon(
    /* [in]  */ WString name,
    /* [in]  */ ProcessId processId,
    /* [in]  */ PEMuid pClsId,
    /* [in]  */ PEIID pIid,
    /* [in]  */ UInt32 uIndex,
    /* [out] */ oid_t * pOid)
{
    Object   * pObject   = NULL;
    NameHook * pNameHook = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());

    Lock(m_hServiceLock);

    pNameHook = NameHook::S_Find(name);
    if (NULL != pNameHook) {
        ec = E_ALREADY_EXIST;
        goto ErrorExit;
    }

    pObject = new Object();
    if (NULL == pObject) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    pNameHook = NameHook::S_Create(pObject, processId, name);
    if (NULL == pNameHook) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    pObject->Initialize(processId, pClsId, uIndex, pNameHook);
    Object::S_AddObject(pObject);

    Unlock(m_hServiceLock);
    NotifyServiceWaiters(name);
    if (NULL != pOid) {
        *pOid = pObject->m_oid;
    }

    return ec;

ErrorExit:
    Unlock(m_hServiceLock);
    if (NULL != pObject) {
        delete pObject ;
        pObject = NULL;
    }
    return ec;
}

ECode CServiceCentral::CreateNamedObject(
        /* [in]  */ WString name,
        /* [in]  */ ProcessId processId,
        /* [in]  */ ObjectType type,
        /* [out] */ Boolean * pbAlreadyExisted,
        /* [out] */ oid_t * pOid)
{
    NameHook * pNameHook = NULL;
    Object   * pObject   = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());
    assert(ObjectType_MetaData != type);

    *pbAlreadyExisted = FALSE;

    Lock(m_hServiceLock);
    pNameHook = NameHook::S_Find(name);
    if (NULL != pNameHook) {
        // if the named already existed.
        if (type == pNameHook->m_pObject->m_objectType) {
            Unlock(m_hServiceLock);
            if (NULL != pOid) {
                *pOid = pNameHook->m_pObject->m_oid;
            }
            *pbAlreadyExisted = TRUE;
            return NOERROR;
        }
        else {
            ec = E_ALREADY_EXIST;
            goto ErrorExit;
        }
    }

    pObject = new Object();
    if (NULL == pObject) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    pNameHook = NameHook::S_Create(pObject, processId, name);
    if (NULL == pNameHook) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    pObject->Initialize(processId, type, pNameHook);
    Object::S_AddObject(pObject);

    Unlock(m_hServiceLock);
    NotifyServiceWaiters(name);

    if (NULL != pOid) {
        *pOid = pObject->m_oid;
    }
    return ec;

ErrorExit:
    Unlock(m_hServiceLock);
    if (NULL != pObject) {
        delete pObject;
        pObject = NULL;
    }
    return ec;
}

ECode CServiceCentral::DeleteNamedObject(
    /* [in]  */ oid_t oid)
{
    NameHook * pNameHook = NULL;

    Lock(m_hServiceLock);
    pNameHook = Object::S_DeleteObject(oid);
    if (NULL == pNameHook) {
        Unlock(m_hServiceLock);
        return NOERROR;
    }

    NameHook::S_Delete(pNameHook);
    Unlock(m_hServiceLock);

    return NOERROR;
}

ECode CServiceCentral::UnregisterCommon(
    /* [in]  */ WString name,
    /* [in]  */ ProcessId processId)
{
    NameHook * pNameHook = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());

    Lock(m_hServiceLock);
    pNameHook = NameHook::S_Find(name);
    if (NULL == pNameHook) {
        ec = E_DOES_NOT_EXIST;
        goto ErrorExit;
    }
    if (processId != pNameHook->m_processId) {
        ec = E_INVALID_OPERATION;
        goto ErrorExit;
    }

    ec = NameHook::S_DeleteAndRelease(pNameHook);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    Unlock(m_hServiceLock);
    return ec;

ErrorExit:
    Unlock(m_hServiceLock);
    return ec;
}

ECode CServiceCentral::ResolveCommon(
    /* [in]  */ WString name,
    /* [out] */ ProcessId * pProcessId,
    /* [out] */ ObjectType * pType,
    /* [out] */ oid_t * pOid,
    /* [out] */ EMuid * pClsId,
    /* [out] */ UInt32 * pIndex)
{
    Object   * pObject   = NULL;
    NameHook * pNameHook = NULL;

    assert(!name.IsNullOrEmpty());
    assert(NULL != pProcessId);
    assert(NULL != pType);
    assert(NULL != pOid);
    assert(NULL != pClsId);
    assert(NULL != pIndex);

    Lock(m_hServiceLock);
    pNameHook = NameHook::S_Find(name);
    if (NULL == pNameHook) {
        Unlock(m_hServiceLock);
        return E_DOES_NOT_EXIST;
    }
    Unlock(m_hServiceLock);

    assert(NULL != pNameHook->m_pObject);
    pObject = pNameHook->m_pObject;

    if (ObjectType_MetaData == pObject->m_objectType) {
        *pClsId  = pObject->m_clsId;
        *pIndex = pObject->m_uIndex;
    }

    *pType      = pObject->m_objectType;
    *pProcessId = pObject->m_processId;
    *pOid       = pObject->m_oid;

    return NOERROR;
}

ECode CServiceCentral::GetProcessIdByServiceOid(
    /* [in]  */ oid_t oid,
    /* [out] */ ProcessId * pProcessId)
{
    Object * pObject = NULL;

    assert(NULL != pProcessId);
    pObject = Object::S_GetObject(oid);
    if (NULL == pObject) {
        return E_DOES_NOT_EXIST;
    }
    *pProcessId = pObject->m_processId;

    return NOERROR;
}

ECode CServiceCentral::RegisterService(
    /* [in]  */ WString name,
    /* [in]  */ ProcessId processId,
    /* [in]  */ oid_t oid)
{
    Object   * pObject   = NULL;
    NameHook * pNameHook = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());

    Lock(m_hServiceLock);
    pNameHook = NameHook::S_Find(name);
    if (NULL != pNameHook) {
        ec = E_ALREADY_EXIST;
        goto ErrorExit;
    }
    pObject = Object::S_GetObject(oid);
    if (NULL == pObject) {
        ec = E_DOES_NOT_EXIST;
        goto ErrorExit;
    }
    // BUGBUG:
    if (NULL != pObject->m_pNameHook) {
        ec = E_ALREADY_EXIST;
        goto ErrorExit;
    }
    pNameHook = NameHook::S_Create(pObject, processId, name);
    if (NULL == pNameHook) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

#if 0   // TODO: Add Code to Attach Server
    if (!pObject->m_pCProcess) {
        // Do nothing
    }
    else {
        assert(pObject->m_pCProcess != GetCurrentProcess());
        AttachServer(oid, CTX_DIFF_PROCESS);
    }
#endif

    pObject->m_pNameHook = pNameHook;
    Unlock(m_hServiceLock);

    NotifyServiceWaiters(name);

    return NOERROR;

ErrorExit:
    Unlock(m_hServiceLock);
    return ec;
}

ECode CServiceCentral::WaitService(
    /* [in]  */ WString name,
    /* [in]  */ Millisecond32 msTimeout,
    /* [out] */ WaitResult * pResult)
{
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());
    assert(NULL != pResult);

    Lock(m_hServiceLock);
    NameHook * pNameHook = NameHook::S_Find(name);
    if (NULL != pNameHook) {
        Unlock(m_hServiceLock);
        *pResult = WaitResult_OK;
        return NOERROR;
    }
    Unlock(m_hServiceLock);

    ServiceWaiter waiter(name);
    ec = waiter.Initialize();
    if (FAILED(ec)) {
        return ec;
    }
    Lock(m_hSrvWaitersListLock);
    m_srvWaitersList.InsertLast(&waiter);
    Unlock(m_hSrvWaitersListLock);

    ec = waiter.TryWait(msTimeout, pResult);
    Lock(m_hSrvWaitersListLock);
    waiter.Detach();
    Unlock(m_hSrvWaitersListLock);

    return ec;
}

ECode CServiceCentral::AttachImportService(
    /* [in] */ ProcessId processId,
    /* [in] */ oid_t oid)
{
    ImportService * pImportService = NULL;

    // Add oid of object to the member of process Struct
    // if the client process abort unnormally, we could correct
    // the count of proxy/stub/object through it
    // NOTE: see the way which is done with it in IKernel->ExitProcess

    pImportService = new ImportService;
    if (NULL == pImportService) {
        return E_OUT_OF_MEMORY;
    }

    // pImportService->m_oid = oid;
    // pImportService->m_pContext = pContext;
    // TODO:

    return NOERROR;
}

ECode CServiceCentral::DetachImportService(
    /* [in] */ ProcessId processId,
    /* [in] */ oid_t oid)
{
    ImportService * pImportService = NULL;

    // Remove oid of object from the member of process Struct
    // and that prove the client process have used the service
    // correctly and we don't need to keep the oid anymore.

    // TODO:

    //assert(pContext == pImportService->m_pContext);
    delete pImportService;

    return NOERROR;
}

ECode CServiceCentral::UnregisterAll(
    /* [in] */ ProcessId processId)
{
    Lock(m_hServiceLock);
    Object::S_DeleteObjects(processId);
    NameHook::S_Delete(processId);
    Unlock(m_hServiceLock);

    return NOERROR;
}

EXTERN ECode STDCALL CreateServiceCentral()
{
    ECode ec = NOERROR;

    ec = Object::S_Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = NameHook::S_Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    s_pServiceCentral = new CServiceCentral();
    if (NULL == s_pServiceCentral) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    ec = s_pServiceCentral->Initialize();
    if (FAILED(ec)) {
        delete s_pServiceCentral;
        s_pServiceCentral = NULL;
        return ec;
    }

    return ec;

ErrorExit:
    if (NULL != s_pServiceCentral) {
        delete s_pServiceCentral;
        s_pServiceCentral = NULL;
    }
    NameHook::S_Destroy();
    Object::S_Destroy();

    return ec;
}

EXTERN void STDCALL DestroyServiceCentral()
{
    if (NULL != s_pServiceCentral) {
        delete s_pServiceCentral;
        s_pServiceCentral = NULL;
    }

    NameHook::S_Destroy();
    Object::S_Destroy();

    return ;
}
